/*
 * Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.huawei.cloudphone.virtualdevice.microphone;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.util.Log;

import com.huawei.cloudphone.jniwrapper.OpusJNIWrapper;
import com.huawei.cloudphone.virtualdevice.common.IVirtualDeviceDataListener;

import java.util.Map;
import java.util.Objects;

public class VirtualMicrophone {
    private static final String TAG = "VirtualMicrophone";
    private static final int AUDIO_INPUT = MediaRecorder.AudioSource.MIC;
    static final int DEFAULT_SAMPLE_RATE = 48000;
    static final int DEFAULT_CHANNEL = 1;
    static final int DEFAULT_BUFFER_SIZE = 320;
    static final String SAMPLE_RATE = "sample_rate";
    static final String MAX_FRAME_SIZE = "max_frame_size";
    static final String CHANNEL = "channel";
    static final String FORMAT = "format";

    private AudioRecord mAudioRecord;
    private int mRecordBuffSize = 320;
    private int mMaxFrameSize;
    private int mSampleRate;
    private int mChannel;
    private int mSampleFormat;
    private boolean mIsReadTaskRunning;
    private ReadThread mThread;
    private IVirtualDeviceDataListener mListener;
    private boolean mIsStart = false;

    public VirtualMicrophone() {
        mAudioRecord = null;
        mRecordBuffSize = DEFAULT_BUFFER_SIZE;
        mSampleRate = DEFAULT_SAMPLE_RATE;
        mChannel = DEFAULT_CHANNEL;
        mSampleFormat = AudioFormat.ENCODING_PCM_16BIT;
        mIsReadTaskRunning = false;
        mListener = null;
    }

    public int start() {
        if (mIsStart) return 0;
        if (OpusJNIWrapper.createOpusEncoder(mSampleRate, DEFAULT_CHANNEL) != 0) {
            return -1;
        }
        mRecordBuffSize = AudioRecord.getMinBufferSize(mSampleRate, mChannel, AudioFormat.ENCODING_PCM_16BIT);
        if (mRecordBuffSize < 0) {
            Log.e(TAG, "Failed to get min buffer size, ret = " + mRecordBuffSize);
            return -1;
        }
        mAudioRecord = new AudioRecord(AUDIO_INPUT, mSampleRate, mChannel, mSampleFormat, mRecordBuffSize);
        try {
            mAudioRecord.startRecording();
        } catch (IllegalStateException e) {
            Log.e(TAG, "start failed, ", e);
            return -1;
        }
        mIsReadTaskRunning = true;
        mThread = new ReadThread();
        mThread.start();
        mIsStart = true;
        return 0;
    }

    public int stop() {
        if (!mIsStart) return 0;
        mIsReadTaskRunning = false;
        try {
            if (mThread != null) {
                mThread.join();
                mThread = null;
            }
            mAudioRecord.stop();
        } catch (IllegalStateException | InterruptedException e) {
            Log.e(TAG, "stop failed, ", e);
            return -1;
        }

        mAudioRecord.release();
        mAudioRecord = null;
        OpusJNIWrapper.destroyOpusEncoder();
        mIsStart = false;
        return 0;
    }

    public void setOnRecvDataListener(IVirtualDeviceDataListener listener) {
        mListener = listener;
    }

    public int setParameters(Map<String, String> params) {
        try {
            mSampleRate = Integer.parseInt(Objects.requireNonNull(params.get(SAMPLE_RATE)));
            if (mSampleRate < 0 ) mSampleFormat = DEFAULT_SAMPLE_RATE;
            mMaxFrameSize = Integer.parseInt(Objects.requireNonNull(params.get(MAX_FRAME_SIZE)));
            mChannel = Integer.parseInt(Objects.requireNonNull(params.get(CHANNEL)));
            if (Integer.parseInt(Objects.requireNonNull(params.get(FORMAT))) == 1)
                mSampleFormat = AudioFormat.ENCODING_PCM_16BIT;
        } catch (RuntimeException e) {
            Log.e(TAG, "setParameters: failed to set param", e);
            return -1;
        }
        Log.i(TAG, "mSampleRate = " + mSampleRate + ", mMaxFrameSize = " + mMaxFrameSize +
                ", mChannel = " + mChannel + ", mSampleFormat = " + mSampleFormat);
        return 0;
    }

    class ReadThread extends Thread {
        @Override
        public void run() {
            short[] bytes = new short[mRecordBuffSize];
            byte[] outBuff = new byte[mRecordBuffSize];
            while (mIsReadTaskRunning) {
                mAudioRecord.read(bytes, 0, bytes.length);
                if (mListener != null) {
                    int outBufLen = OpusJNIWrapper.opusEncode(bytes, bytes.length, outBuff);
                    if (outBufLen > 0) {
                        mListener.onRecvData(outBuff, 0, outBufLen);
                    }
                }
            }
        }
    }

}
